package co.baiku.boot.weixin;

import co.baiku.boot.AjavaerCache;
import co.baiku.boot.cache.service.CacheService;
import co.baiku.boot.common.message.Message;
import co.baiku.boot.common.tools.*;
import co.baiku.boot.config.WeixinConfig;
import co.baiku.boot.config.WeixinPublicConfig;
import co.baiku.boot.exception.WeixinException;
import co.baiku.boot.handle.WeixinPublicHandle;
import co.baiku.boot.message.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 微信公众号
 */
@Component
@ConditionalOnProperty("ajavaer.weixin.public.appid")
public class WeixinPublic {
    private Logger log = LoggerFactory.getLogger(WeixinPublic.class);
    @Resource
    private WeixinPublicConfig weixinPublicConfig;
    @Resource
    private WeixinConfig weixinConfig;

    public WeixinPublic(WeixinPublicConfig weixinPublicConfig, WeixinConfig weixinConfig) {
        this.weixinPublicConfig = weixinPublicConfig;
        this.weixinConfig = weixinConfig;
    }

    /**
     * 服务器URL配置验证
     *
     * @param signature
     * @param timestamp
     * @param nonce
     * @param echostr
     * @return
     */
    public Message<String> ckeckSign(String signature, String timestamp, String nonce, String echostr) {
        String[] arr = new String[]{weixinPublicConfig.getToken(), timestamp, nonce};
        // 将token、timestamp、nonce三个参数进行字典序排序
        StringTools.sort(arr);
        StringBuilder content = new StringBuilder();
        for (int i = 0; i < arr.length; i++) {
            content.append(arr[i]);
        }
        MessageDigest md = null;
        String tmpStr = null;

        try {
            md = MessageDigest.getInstance("SHA-1");
            // 将三个参数字符串拼接成一个字符串进行sha1加密
            byte[] digest = md.digest(content.toString().getBytes(StringTools.UTF8));
            tmpStr = StringTools.byteToStr(digest);
        } catch (NoSuchAlgorithmException e) {
            log.error(e.getMessage(), e);
        }
        if (tmpStr != null && tmpStr.equals(signature.toUpperCase())) {
            return Message.result(echostr);
        }
        return Message.fail(HttpStatus.FORBIDDEN, "验证失败");
    }

    /**
     * 获得accessToken,如果不存在,则从微信服务器获取一个,并缓存起来,缓存有效期7200秒,如果获取失败,则返回错误信息
     *
     * @return
     */
    public Message<String> getAccessToken() {
        Message<String> result = Message.success("OK");
        CacheService cacheHandle = AjavaerCache.engine(weixinConfig.getCacheEngine());
        String accessToken = cacheHandle.get(weixinPublicConfig.getAccessTokenCacheKey());
        String url = weixinPublicConfig.getAccess_token_url();
        Message<WeixinJssdkAccessToken> accessTokenResult = HttpTools.get(url, WeixinJssdkAccessToken.class);
        log.debug("Get Access Token From Wx,url:{},res:{}", url, JsonTools.beanToJson(accessTokenResult));
        if (accessTokenResult.isSuccess()) {
            WeixinJssdkAccessToken resultData = accessTokenResult.getResult();
            if (resultData.success()) {
                accessToken = resultData.getAccess_token();
                cacheHandle.put(weixinPublicConfig.getAccessTokenCacheKey(), resultData.getAccess_token(), resultData.getExpires_in(), TimeUnit.SECONDS);
            } else {
                result.setMsg("get AccessToken error:" + resultData.getErrmsg());
                result.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
                log.error("weixin error:{}", JsonTools.beanToJson(resultData));
            }
        } else {
            result.setMsg(accessTokenResult.getMsg());
            result.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
            log.error("connect weixin server error:{}", accessTokenResult.getMsg());
        }
        result.setResult(accessToken);
        return result;
    }

    /**
     * 获取用户基本信息(UnionID机制)
     *
     * @param openId 用户openId
     * @return
     * @see <a href='https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183'>获取用户基本信息(UnionID机制)</a>
     */
    public WeixinUserInfo userInfo(String openId) throws WeixinException {
        Message<String> accessToken = getAccessToken();
        if (accessToken.isSuccess()) {
            String user_info_url = weixinPublicConfig.getUser_info_url(accessToken.getResult(), openId);
            Message<WeixinUserInfo> weixinUserInfoMessage = HttpTools.get(user_info_url, WeixinUserInfo.class);
            if (weixinUserInfoMessage.isSuccess()) {
                if (weixinUserInfoMessage.getResult().getErrcode() != null && weixinUserInfoMessage.getResult().getErrcode() == 40001) {
                    log.error("获取用户信息失败,AccessToken:{}", accessToken.getResult());
                }
                return weixinUserInfoMessage.getResult();
            } else {
                log.error("获取用户信息失败:" + JsonTools.beanToJson(weixinUserInfoMessage));
            }
        }
        throw new WeixinException("AccessToken获取失败");
    }

    /**
     * 开发者可通过该接口来批量获取用户基本信息。最多支持一次拉取100条。
     *
     * @param openIds 用户openId列表
     * @return
     */
    public WeixinUserInfoList userInfoBatchGet(List<String> openIds) throws WeixinException {
        WeixinUserInfoList result = new WeixinUserInfoList(new ArrayList<>());
        if (ObjectTools.isBlank(openIds)) {
            return result;
        }
        Message<String> accessToken = getAccessToken();
        if (accessToken.isSuccess()) {
            String user_info_batchget_url = weixinPublicConfig.getUser_info_batchget_url(accessToken.getResult());
            String lang = "zh_CN";
            List<Map<String, String>> itemData = new ArrayList<>();
            openIds.forEach(openId -> {
                Map<String, String> item = new HashMap<>();
                item.put("openid", openId);
                item.put("lang", lang);
                itemData.add(item);
            });
            Map<String, Object> data = new HashMap<>();
            data.put("user_list", itemData);
            Message<WeixinUserInfoList> weixinUserInfoListMessage = HttpTools.postJson(user_info_batchget_url, JsonTools.beanToJson(data), WeixinUserInfoList.class);
            if (weixinUserInfoListMessage.isSuccess()) {
                return weixinUserInfoListMessage.getResult();
            } else {
                log.error("批量获得用户信息失败 code:{} msg:{}", weixinUserInfoListMessage.getCode(), weixinUserInfoListMessage.getMsg());
            }
            return result;
        }
        throw new WeixinException("AccessToken获取失败");
    }

    /**
     * 获取用户列表
     * <p>公众号可通过本接口来获取帐号的关注者列表，关注者列表由一串OpenID（加密后的微信号，每个用户对每个公众号的OpenID是唯一的）组成。一次拉取调用最多拉取10000个关注者的OpenID，可以通过多次拉取的方式来满足需求。</p>
     *
     * @param nextOpenId 下一个openId
     * @return
     * @see <a href='https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140840'>获取用户列表</a>
     */
    public WeixinOpenIdPage userGet(String nextOpenId) throws WeixinException {
        if (nextOpenId == null) {
            nextOpenId = "";
        }
        Message<String> accessToken = getAccessToken();
        if (accessToken.isSuccess()) {
            String user_get_url = weixinPublicConfig.getUser_get_url(accessToken.getResult(), nextOpenId);
            Message<WeixinOpenIdPage> weixinOpenIdPageData = HttpTools.get(user_get_url, WeixinOpenIdPage.class);
            if (weixinOpenIdPageData.isSuccess()) {
                return weixinOpenIdPageData.getResult();
            } else {
                log.error("获取用户列表失败:" + JsonTools.beanToJson(weixinOpenIdPageData));
            }
        }
        throw new WeixinException("AccessToken获取失败");
    }

    /**
     * 接收公众号消息
     */
    public void publicMsg(String message, WeixinPublicHandle weixinPublicHandle) throws Exception {
        Map<String, String> map = XmlTools.xmlToMap(message);
        WeixinPublicMessage publicMessage = new WeixinPublicMessage(map);
        weixinPublicHandle.onMessage(publicMessage);
    }

    /**
     * 查询菜单
     *
     * @return
     */
    public WeixinMenuGroup getMenu() throws WeixinException {
        Message<String> accessToken = getAccessToken();
        if (accessToken.isSuccess()) {
            String get_menu_url = weixinPublicConfig.getGet_menu_url(accessToken.getResult());
            Message<WeixinMenuGroup> group = HttpTools.get(get_menu_url, WeixinMenuGroup.class);
            WeixinMenuGroup data = group.getResult();
            if (data.success()) {
                return data;
            } else {
                throw new WeixinException(data.getErrmsg());
            }
        }
        throw new WeixinException("AccessToken获取失败");
    }

    /**
     * 创建菜单
     *
     * @return
     * @see <a href="https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141014">自定义菜单查询接口</a>
     */
    public Message<WeixinMessage> createMenu(WeixinMenu weixinMenu) throws WeixinException {
        Message<String> accessToken = getAccessToken();
        if (accessToken.isSuccess()) {
            String create_menu_url = weixinPublicConfig.getCreate_menu_url(accessToken.getResult());
            return HttpTools.postJson(create_menu_url, JsonTools.beanToJson(weixinMenu), WeixinMessage.class);
        }
        throw new WeixinException("AccessToken获取失败");
    }

    /**
     * 删除菜单
     *
     * @return
     */
    public Message<WeixinMessage> deleteMenu() throws WeixinException {
        Message<String> accessToken = getAccessToken();
        if (accessToken.isSuccess()) {
            String delete_menu_url = weixinPublicConfig.getDelete_menu_url(accessToken.getResult());
            return HttpTools.get(delete_menu_url, WeixinMessage.class);
        }
        throw new WeixinException("AccessToken获取失败");
    }

    /**
     * 发送模版消息
     *
     * @param message
     * @return
     * @throws WeixinException
     * @see <a href="https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Interface.html">模版消息</a>
     */
    public Message<WeixinTemplateMessageResponse> sendTemplateMessage(WeixinTemplateMessageRequest message) throws WeixinException {
        Message<String> accessToken = getAccessToken();
        if (accessToken.isSuccess()) {
            String templateSendUrl = weixinPublicConfig.getTemplate_send_url(accessToken.getResult());
            return HttpTools.postJson(templateSendUrl, JsonTools.beanToJson(message), WeixinTemplateMessageResponse.class);
        }
        throw new WeixinException("AccessToken获取失败");
    }

}
